package taskprocess

import (
	"acs/comet/client"
	"acs/comet/config"
	"acs/comet/proto"
	"acs/pbmodel"
	"acs/util"
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
	"sync"
	"time"

	clientMap "acs/comet/client"
)

// HandleEvent 保存event信息，并根据event信息向在线客户端推送消息。
func HandleEvent(task *EventTask) (err error) {
	//如果uid列表是空的就直接返回
	if len(task.TargetUID) == 0 {
		return
	}
	startTime := time.Now()
	t1 := time.Now()
	t2 := time.Now()
	defer func() {
		eventLogger.Infow("HandleEvent",
			"task.TaskID", task.TaskID,
			"task.TargetUID", task.TargetUID,
			"task.Event", task.Event,
			"task.Callback", task.Callback,
			"t1", t1.Sub(startTime).Seconds(),
			"t2", t2.Sub(t1).Seconds(),
			"err", err)
	}()

	logger.Infof("Preparing handling event. Task: %+v", *task)
	taskLock := &sync.Mutex{}
	var readyIds []string
	failIds := map[string]string{}
	readyIDMap := map[string]pbmodel.RegisterInfo{}

	upFn := func(c *client.Client, taskI interface{}) {
		defer func() {
			util.PrintPanicStack()
		}()
		regInfo := c.GetRegisterInfo()
		task := taskI.(*EventTask)

		//根据过滤条件判断当前用户是否是需要发送的用户
		if !filterUser(task, regInfo) {
			failIds[regInfo.GetUID()] = fmt.Sprintf("Client[%v] filterUser: %v", c.GetRemoteAddr(), regInfo.GetUID())
			return
		}

		logger.Debugf("Sending event task to user[%v]: %+v", regInfo.UID, proto.CmdEvent)
		eventResp := c.SendCmd(proto.CmdEvent, &task.Event, &pbmodel.EventResp{})
		if eventResp.Err != nil {
			//如果出现错误那么user_task 状态不变, 如果全部成功那么就修改状态为已发送
			logger.Warnf("Event failed to [%v]: %v", c.GetRemoteAddr(), eventResp.Err)
			failIds[regInfo.GetUID()] = fmt.Sprintf("Client[%v] event failed resp: %v", c.GetRemoteAddr(), eventResp.Err)
			return
		} else if respData, ok := eventResp.RespData.(*pbmodel.EventResp); ok {
			if respData.Status == nil || *respData.Status != 6000 {
				failIds[regInfo.GetUID()] = fmt.Sprintf("Client[%v] event failed status: %+v", c.GetRemoteAddr(), respData.Status)
				logger.Warnf("Client[%v] event failed: %v:%+v", c.GetRemoteAddr(), respData.Status)
				return
			}
		} else {
			failIds[regInfo.GetUID()] = fmt.Sprintf("Client[%v] event failed respData: %+v", c.GetRemoteAddr(), eventResp.RespData)
			logger.Warnf("Client[%v] event failed: %v:%v", c.GetRemoteAddr(), eventResp.RespData)
			return
		}

		//执行完后将执行的uid放到slice中
		taskLock.Lock()
		readyIDMap[regInfo.GetUID()] = regInfo
		readyIds = append(readyIds, *regInfo.UID)
		taskLock.Unlock()
	}

	if task.TargetUID[0] == "*" {
		clientMap.Clients.Range(func(uid string, c *client.Client) bool {
			upFn(c, task)
			return false
		})
	} else {
		for i := 0; i < len(task.TargetUID); i++ {
			c := clientMap.Clients.Get(task.TargetUID[i])
			if c != nil {
				upFn(c, task)
			} else {
				failIds[task.TargetUID[i]] = "no found"
			}
		}
	}

	t1 = time.Now()

	var ids []string
	//status 发送状态 0 发送未成功，1 发送成功, 同时allSend 1 表示全部发送,0 消息是部分发送
	var allSend int
	if strings.Trim(task.TargetUID[0], " ") == "*" {
		allSend = 1
		ids = append(readyIds, "*") //添加一个*的用户任务用来保存条件
	} else {
		//如果不是全局消息，那么存的是失败列表(包括发送失败的和过滤失败的，因为现在过滤失败但是有可能下次连得时候符合，那么也需要发送。)
		ids = SliceRemove(task.TargetUID, readyIds)
	}

	//只有有uid存在的时候才进行存储
	if len(task.TargetUID) > 0 {
		//构建存储对象
		eventTaskToStore := EventTaskToStore{}
		eventTaskToStore.Label = task.Label
		eventTaskToStore.Content = task.Content
		eventTaskToStore.Expiration = time.Now().Add(time.Duration(config.Conf.EventExpiration) * time.Second).Unix()
		eventTaskToStore.AllSend = allSend

		//存入mongodb中，*已发送列表 status=1  uid未发送列表 status=0
		err = StoreEventTaskToDb(&eventTaskToStore, task, ids, allSend)
		if err != nil {
			logger.Warnf("Failed to handle event task. ERR: %v", err)
		}
	}
	t2 = time.Now()
	logger.Infof("Success to handle event post. Task: %+v", *task)

	callback := &eventTaskCallBack{
		uri:      task.Callback,
		TaskID:   task.TaskID,
		Success:  readyIds,
		UIDsInfo: readyIDMap,
		Fail:     failIds,
	}
	go callback.post()

	return
}

type eventTaskCallBack struct {
	uri      string      `json:"-"`
	TaskID   string      `json:"taskid"`
	Fail     interface{} `json:"fail_uids,omitempty"`
	Success  []string    `json:"success_uids,omitempty"`
	UIDsInfo interface{} `json:"uids_info,omitempty"`
}

func (c *eventTaskCallBack) post() error {
	if c.uri == "" {
		return nil
	}
	data, e := json.Marshal(c)
	if e != nil {
		logger.Errorf("Failed to handle event post. ERR: %v", e)
		return e
	}
	buffer := bytes.NewBuffer(data)

	t := []time.Duration{0, 2, 3, 5, 8}
	var err error
	var body []byte
	for i := 0; i < len(t); i++ {
		time.Sleep(time.Second * t[i])
		resp, e := http.Post(c.uri, "application/json", buffer)
		if e != nil {
			err = e
			continue
		} else if resp.StatusCode != http.StatusOK {
			body, _ = ioutil.ReadAll(resp.Body)
			resp.Body.Close()
			err = fmt.Errorf("bad resp code: %d", resp.StatusCode)
			continue
		}
		body, _ = ioutil.ReadAll(resp.Body)
		resp.Body.Close()
		err = nil
		break
	}
	if err != nil {
		logger.Errorf("Failed to handle event post. ERR: %v", err)
	}
	eventLogger.Infow("HandleEvent",
		"uri", c.uri,
		"data", string(data),
		"ret", string(body),
		"err", err)
	return err
}

//删除指定items,并且可以删除重复id
func SliceRemove(slice []string, remove []string) (result []string) {

	m := make(map[string]bool)
	for _, v := range remove {
		v = strings.Trim(v, " ")
		m[v] = true
	}

	for _, val := range slice {
		val = strings.Trim(val, " ")
		if _, ok := m[val]; !ok {
			result = append(result, val)
		}
	}
	return
}

func filterUser(task *EventTask, regInfo pbmodel.RegisterInfo) (isTarget bool) {

	//状态是否发送
	var isTargetClient bool
	//如果第一个是*
	headUid := strings.Trim(task.TargetUID[0], " ")
	if headUid != "*" {
		for _, uid := range task.TargetUID {
			if strings.Trim(uid, " ") == *regInfo.UID {
				isTargetClient = true
				break
			}
		}
	} else {
		isTargetClient = true
	}

	if !isTargetClient {
		return
	}

	//appver start  *就不进行过滤了
	if task.Appver[0] != "*" && VersionOrdinal(task.Appver[0]) > VersionOrdinal(*regInfo.Appver) {
		return
	}
	//appver end
	if task.Appver[1] != "*" && VersionOrdinal(task.Appver[1]) < VersionOrdinal(*regInfo.Appver) {
		return
	}

	//bundleid 等于0的情况为没有过滤条件
	if len(task.BundleIds) > 0 {
		if ok, _ := InArrayString(*regInfo.BundleID, task.BundleIds); !ok {
			return
		}
	}

	//system
	if len(task.System) > 0 {
		if ok, _ := InArrayInt(int(*regInfo.System), task.System); !ok {
			return
		}
	}

	//osversion start
	if task.OSVersion[0] != "*" && VersionOrdinal(task.OSVersion[0]) > VersionOrdinal(*regInfo.OSVersion) {
		return
	}
	//osversion end
	if task.OSVersion[1] != "*" && VersionOrdinal(task.OSVersion[1]) < VersionOrdinal(*regInfo.OSVersion) {
		return
	}

	//brand
	if len(task.Brand) > 0 {
		if ok, _ := InArrayString(*regInfo.Brand, task.Brand); !ok {
			return
		}
	}

	//model
	if len(task.Model) > 0 {
		if ok, _ := InArrayString(*regInfo.Model, task.Model); !ok {
			return
		}
	}

	isTarget = true

	return
}

/**
字符串是否在slice中
如果在exists返回true, index返回第几个元素
*/
func InArrayString(val string, array []string) (exists bool, index int) {
	exists = false
	index = -1

	for i, v := range array {
		if val == v {
			index = i
			exists = true
			return
		}
	}

	return
}

/**
数字是否在slice中
如果在exists返回true, index返回第几个元素
*/
func InArrayInt(val int, array []int) (exists bool, index int) {
	exists = false
	index = -1

	for i, v := range array {
		if val == v {
			index = i
			exists = true
			return
		}
	}

	return
}

/**
将版本号字符串进行转换，然后就可以根据字符串直接判断大小

*/
func VersionOrdinal(version string) string {
	// ISO/IEC 14651:2011
	const maxByte = 1<<8 - 1
	vo := make([]byte, 0, len(version)+8)
	j := -1
	for i := 0; i < len(version); i++ {
		b := version[i]
		if '0' > b || b > '9' {
			vo = append(vo, b)
			j = -1
			continue
		}
		if j == -1 {
			vo = append(vo, 0x00)
			j = len(vo) - 1
		}
		if vo[j] == 1 && vo[j+1] == '0' {
			vo[j+1] = b
			continue
		}
		if vo[j]+1 > maxByte {
			panic("VersionOrdinal: invalid version")
		}
		vo = append(vo, b)
		vo[j]++
	}
	return string(vo)
}
